Skip to content

面试官: Memory 系统怎么设计?短期和长期记忆分别怎么管理?

你的回答:

  • Memory 系统分短期和长期两层。短期记忆管理当前对话历史,长期记忆持久化保存重要信息。
  • 短期记忆用滑动窗口,保留最近 5-10 轮对话,旧的自动丢弃。同时做 Token 预算管理,给不同类型信息分配额度,超额就压缩。我们还会把关键信息(任务目标、用户偏好)置顶到 System Prompt,保证不被截断。
  • 长期记忆用向量数据库存储用户画像、历史对话摘要、领域知识。用户新提问时,用问题向量检索最相关的记忆注入上下文,这个机制和 RAG 是一样的。同时用 MySQL 存储结构化信息,比如用户档案、任务记录。
  • 摘要压缩方面,我们用分层摘要:最近 5 轮保留完整对话,6-15 轮保留摘要,15 轮以前只保留关键事实。同时做渐进式压缩,每次新增对话检查 Token 数,超阈值就把最旧的几轮压缩成摘要。
  • 我们还解决了记忆污染问题。记忆有时间衰减,越旧的权重越低。同时把记忆分成永久偏好、临时偏好、历史行为三类,临时偏好优先级最高,避免旧信息干扰新任务。

面试官追问: 记忆越多越好吗?怎么防止旧信息干扰?

你的回答:

  • 记忆不是越多越好。历史越长,Token 成本越高,LLM 越容易被无关信息干扰,答案质量反而下降。而且旧信息可能和当前任务无关,甚至产生误导。
  • 我们用三个方法防止旧信息干扰。
  1. 第一是时间衰减,检索记忆时除了语义相似度还考虑时间因素。最近 7 天的记忆权重 1.0,30 天以前的权重 0.1。
  2. 第二是记忆分类。永久偏好(过敏信息)、临时偏好(这次想吃什么)、历史行为分开管理,临时偏好优先级最高。用户明确说「我现在想吃日料」,就覆盖之前「喜欢粤菜」的记忆。
  3. 第三是相关性过滤。不是把所有记忆都塞给 LLM,而是根据当前问题检索最相关的 Top-5,无关记忆不注入。我们统计过,注入 5 条相关记忆比注入 20 条历史记忆效果更好,成本还降低 60%。

可以把 Agent Memory 系统设计成一个分层、分级、可检索、可衰减的架构,核心目标不是“存得越多越好”,而是在有限上下文里,让最该出现的信息稳定出现,让不该出现的旧信息尽量别干扰推理

这个方案可以直接拆成六部分:总体架构、短期记忆、长期记忆、写入机制、读取机制、抗污染机制

一、总体架构

整个 Memory 系统建议分成四层:

1.工作记忆层(Working Memory)

这是最靠近当前推理的一层,直接服务本轮任务执行。

里面放的是:

  • 当前任务目标
  • 当前轮用户输入
  • 当前子任务状态
  • 当前工具调用结果
  • 当前约束条件

它的特点是实时、短生命周期、高优先级
一般直接拼进 prompt,或者放进 agent runtime state。


2.短期记忆层(Short-term Memory)

用于维护最近几轮对话和上下文连续性。

里面放的是:

  • 最近 N 轮对话
  • 最近几步推理结论
  • 最近工具调用摘要
  • 当前会话内形成的临时偏好

这层适合做成滑动窗口 + 分层压缩


3.长期记忆层(Long-term Memory)

用于跨会话持久化。

里面放的是:

  • 用户长期偏好
  • 用户画像
  • 历史任务经验
  • 关键事实
  • 领域知识
  • 历史总结

这层不能直接全塞给模型,而是要通过检索召回进入上下文。


4.元数据与治理层(Memory Governance)

这一层负责控制记忆质量。

包含:

  • 记忆分类
  • 权重管理
  • 时间衰减
  • 冲突检测
  • 失效机制
  • 删除与更新策略
  • 隐私与权限控制

真正让系统可用的,往往不是“存储”,而是这层治理能力。


二、短期记忆怎么设计

短期记忆的核心不是完整保存所有聊天记录,而是让当前任务所需信息在 Token 限额内保持连续性

方案一:滑动窗口

保留最近 5 到 12 轮对话。
比如默认保留最近 5 轮。

但不能只是机械保留,因为不同消息长度差别很大,所以更推荐:

  • 按轮数限制
  • 再叠加 Token 预算限制

例如:

  • 最近对话窗口上限:10 轮
  • 短期记忆总预算:3000 tokens
  • 超过预算后触发压缩

方案二:分层保留

短期记忆建议按“新鲜度”分层:

  • 最近 1 到 5 轮:保留原文
  • 6 到 15 轮:保留摘要
  • 更早内容:只保留关键事实

这比纯窗口删除更稳,因为它不会把重要上下文直接丢光。


方案三:置顶关键上下文

有些信息不能随着窗口滚动丢失,需要置顶到固定槽位,比如:

  • 当前任务目标
  • 成功标准
  • 用户明确约束
  • 当前偏好
  • 当前会话中的关键决策

这些内容不要和普通聊天历史混在一起,而要单独作为:

  • pinned memory
  • session state
  • system slot

这样可以避免长对话后“任务目标漂移”。


三、长期记忆怎么设计

长期记忆的重点是:持久化 + 可检索 + 可更新

通常建议分成两类存储:

1.向量存储

适合放非结构化内容:

  • 历史对话摘要
  • 用户表达过的偏好
  • 经验片段
  • 领域知识片段
  • 任务复盘结论

每条记忆可以存成一个 memory chunk,字段类似:

  • memory_id
  • user_id
  • text
  • summary
  • embedding
  • type
  • importance
  • confidence
  • created_at
  • last_access_at
  • expires_at
  • source
  • scope

这部分适合放在向量数据库里,供语义检索。


2.结构化存储

适合放稳定字段:

  • 用户档案
  • 永久偏好
  • 黑名单/禁忌
  • 历史任务记录
  • 会话索引
  • 记忆版本
  • 冲突关系

这部分建议放 MySQL / PostgreSQL 一类关系型数据库。

原因很简单:
结构化信息更新、覆盖、查询、约束都更容易做,不能全靠向量库。


四、记忆的分类设计

长期记忆不要放成一个大桶,至少要分成下面几类:

1.永久偏好

长期稳定,不轻易变化,比如:

  • 语言偏好
  • 表达风格偏好
  • 长期职业背景
  • 不可违反的禁忌条件

这类权重高、有效期长。


2.临时偏好

只在一段时间内成立,比如:

  • 这次项目想用 React
  • 今天想吃日料
  • 这轮任务里先要中文输出

这类记忆必须带 TTL 或会话范围。
否则最容易污染后续任务。


3.历史行为

是用户做过什么,不代表现在还想这么做,比如:

  • 以前选过某个方案
  • 曾经关注某个方向
  • 做过某类任务

这类信息只能做辅助特征,不能直接当当前约束。


4.任务知识

来自过去任务的经验:

  • 哪种方案成功率高
  • 某类 bug 的修复经验
  • 某项目的上下文摘要

适合做跨任务复用。


5.事实记忆

稳定客观事实,比如:

  • 用户学校
  • 技术栈
  • 常用平台

如果是高置信长期事实,可以写入 profile 表并做版本管理。


五、写入机制怎么设计

不是每轮对话都要写长期记忆。
Memory 系统最重要的是写入门控

写入原则

只有满足以下条件的信息才值得入长期记忆:

  • 跨会话可能有价值
  • 对未来任务有帮助
  • 相对稳定
  • 重要性高
  • 非瞬时噪声

写入流程

第一步:候选提取

从当前对话中抽取候选记忆:

  • 用户偏好
  • 用户约束
  • 新增事实
  • 关键任务结论
  • 经验性规则

这一步可以让 LLM 做 extraction(提取),也可以规则+模型结合。

第二步:分类与打分

给候选记忆打标签:

  • type:永久/临时/行为/事实/经验
  • importance:1-5
  • confidence:0-1
  • ttl:是否过期
  • scope:全局/会话/任务

第三步:去重与冲突检测

写入前先检查:

  • 是否与已有记忆重复
  • 是否与已有记忆冲突
  • 是否需要覆盖旧值
  • 是否需要保留版本

比如: 旧记忆:用户喜欢粤菜
新记忆:我现在想吃日料

这不是事实冲突,而是临时偏好覆盖当前场景,所以处理方式应是:

  • 不删除长期偏好
  • 在当前 session 写入高优先级临时偏好
  • 读取时临时偏好优先

第四步:持久化

根据类型分别写入:

  • 结构化字段 → SQL
  • 非结构化摘要 → 向量库

六、读取机制怎么设计

Memory 的价值不在存,而在取。
读取建议采用“多路召回 + 重排序 + 注入控制”的流程。

第一步:查询理解

先从当前用户请求中提取检索意图,例如:

  • 当前任务主题
  • 用户偏好需求
  • 是否涉及历史任务
  • 是否需要事实类记忆还是经验类记忆

第二步:多路召回

不要只做一次向量检索,而是做分通道召回:

  • 通道 A:永久偏好
  • 通道 B:临时偏好
  • 通道 C:历史任务摘要
  • 通道 D:相关经验片段
  • 通道 E:会话内短期记忆

这样召回结果可控,不会全靠一个向量相似度。

第三步:混合排序

记忆排序不要只按 embedding 相似度,而要用综合评分:

[  
Score = \alpha \cdot Similarity + \beta \cdot Importance + \gamma \cdot Recency + \delta \cdot Confidence + \epsilon \cdot TypePriority  
]

其中:

  • Similarity:语义相似度
  • Importance:重要程度
  • Recency:新近性
  • Confidence:置信度
  • TypePriority:类型优先级

比如:

  • 临时偏好优先于历史行为
  • 永久禁忌优先于普通经验
  • 最近 7 天权重大于 30 天前

第四步:Top-K 控制

不要把所有召回结果都注入模型。
通常只取 Top-3Top-8

经验上:

  • 注入 5 条高相关记忆,往往比注入 20 条历史记忆效果更好
  • 成本更低
  • 噪声更小
  • 推理更稳定

第五步:上下文注入

注入时也不要原样堆进去,而要结构化组织:

  • 当前任务目标
  • 当前会话约束
  • 检索到的相关偏好
  • 检索到的历史经验
  • 必须遵守的事实约束

也就是说,Memory 注入应该是“整理后的上下文包”,不是“原始记忆拼盘”。


七、摘要压缩怎么做

摘要压缩是短期记忆和长期记忆的中间桥梁。

推荐用分层摘要 + 渐进式压缩

分层摘要

把历史对话按层保存:

  • L0:原始消息
  • L1:会话摘要
  • L2:关键事实摘要
  • L3:长期经验摘要

渐进式压缩

每次新增对话时检查上下文 Token 数:

  • 未超限:不处理
  • 接近阈值:把最旧的若干轮压成摘要
  • 继续增长:把旧摘要再压成关键事实

这种方式比“一刀切总结全部历史”更稳定,因为它避免了大规模信息损失。


八、怎么防止旧信息干扰

这部分是面试里最关键的点,因为真正难的不是“有记忆”,而是“记忆不污染”。

1.时间衰减

越旧的记忆默认权重越低。

例如:

  • 7 天内:1.0
  • 8 到 30 天:0.6
  • 30 天以上:0.1

但这不是绝对规则。
某些永久偏好、禁忌类记忆,即使旧也应该保持高权重。

所以更准确的做法是:

时间衰减 × 类型修正

2.按类型隔离

不同类别记忆不要混用:

  • 永久偏好:长期有效
  • 临时偏好:高优先级,短时有效
  • 历史行为:仅参考
  • 经验知识:按主题召回

这能避免“曾经做过”被误判成“现在要做”。

3.相关性过滤

只注入与当前问题真正相关的记忆。
不相关的记忆即使重要,也不要进入本轮上下文。

4)冲突检测

对新旧记忆建立冲突关系。

例如:

  • 旧:默认用英文输出
  • 新:这次用中文输出

则系统需要知道这是“局部覆盖”,不是永久删除。
读取时根据 scope 选择更近、更局部的版本。

5.TTL 与失效机制

临时偏好、任务状态、会话缓存都应该设置失效时间。

例如:

  • 本轮项目技术栈选择:会话结束失效
  • 今天的餐饮偏好:24 小时失效
  • 长期技能背景:长期有效

6.用户显式覆盖优先

当用户明确表达新意图时,应允许覆盖旧记忆。 比如用户说:

  • “这次不要按我以前的风格来”
  • “先忽略之前方案”
  • “我现在改主意了”

系统应立刻把当前声明视为高优先级上下文,而不是仍被长期记忆牵着走。

九、一个可落地的数据结构示例

每条记忆可以设计成这样:

json
{
  "memory_id": "mem_001",
  "user_id": "u_123",
  "content": "用户当前希望本次项目优先使用 React 技术栈",
  "summary": "本次项目技术栈偏好:React",
  "type": "temporary_preference",
  "scope": "session",
  "importance": 4,
  "confidence": 0.95,
  "created_at": "2026-03-24T10:00:00Z",
  "last_access_at": "2026-03-24T10:10:00Z",
  "expires_at": "2026-03-25T10:00:00Z",
  "source": "dialogue_extraction",
  "embedding": "...",
  "status": "active"
}

结构化表可以再单独存:

  • user_profile
  • user_preferences
  • task_history
  • memory_conflicts
  • session_state

十、一个典型请求的运行流程

当用户发来一个新问题时,系统流程可以这样走:

  1. 解析当前问题,识别任务目标与约束
  2. 从短期记忆中取最近对话和当前会话状态
  3. 从长期记忆中分通道召回候选记忆
  4. 用相似度、时间、重要性、类型做混合重排
  5. 选 Top-K 记忆注入 prompt
  6. 模型生成回答
  7. 回答结束后抽取新的候选记忆
  8. 做分类、去重、冲突检测、持久化
  9. 更新短期窗口与会话摘要

这就是一个完整闭环。

十一、这个方案的核心设计原则

这个 Agent Memory 系统最重要的原则可以总结成四句:

短期记忆保证连续性,长期记忆保证可积累;
写入要有门槛,读取要有筛选;
旧记忆不能无条件保留,新记忆不能无条件覆盖;
真正有效的记忆系统,不是“记得多”,而是“该想起时想得对”。


十二、面试中可以直接落地成一句总结

如果你要把它压成比较像面试里的方案表达,可以这样说:

我会把 Agent Memory 设计成短期记忆、长期记忆和治理层三部分。短期记忆用滑动窗口加分层摘要,保证当前任务上下文连续;长期记忆用向量库存非结构化记忆、关系库存结构化档案,通过语义检索加时间衰减、类型优先级、重要性重排来做 Top-K 注入;同时在写入侧做候选提取、分类、去重和冲突检测,在读取侧做相关性过滤和 TTL 控制,避免旧信息污染当前推理。

评论区

欢迎留言、补充或勘误。

xiaoba.blog